Hey there.
Welcome back.
In this lecture we're going to be exploring a service called the Run Service.
This is a crucial and fundamental service for being able to access methods and events for time management
of your code, and also checking what the context of your code is executing it.
It has several events that get fired at different points in the creation of a single frame.
So the service allows you to be able to execute code every single frame.
The documentation states that the run service contains methods and events for time management, as well
as for managing the context in which a game or script is running.
Methods like is client, is server is studio can help you determine under what context code is running.
These methods are useful for module scripts that may be required by both client and server scripts.
Furthermore, is studio can be used to add special behaviors for in studio testing.
Run service also houses events that allow your code to adhere to Roblox frame by frame loop, such as
pre animation, pre simulation, heartbeat, and Pre-render.
Selecting the proper event to use for any case is important, so you should read the task scheduler
documentation to make an informed decision.
If we take a look at the documentation about the task scheduler, this is the system that's responsible
for basically scheduling all the tasks and things that need to execute in your game.
And if we scroll down, there's going to be a picture right here that basically shows us how a single
frame is created.
It says the task scheduler categorizes and completes tasks in the following order.
Some tasks may not perform work in a frame, while others may run multiple times.
So the first thing that's going to happen when a new frame is being created is that we're first going
to listen to any user input.
So it says input events.
The user input service and bound functions to the context action service are handled first, such as
key presses, mouse movements, touches, gamepad vibration, etc. then we go into the rendering phase
now before the screen is rendered so things are drawn on the screen.
It's going to fire an event called a render step, and it's also going to go through and execute a bunch
of functions bound to the render step event.
It says all functions bound with the bind to render step method are called in priority order.
In the built in player module, the camera and character control scripts bind callbacks at certain priorities
determined by the render priority enum, and then it's going to fire the render stepped event.
It says it's fired, and as with all events, connected functions are called with the most recently
connected callback ran first and then after the screen is drawn and rendered, it says any further changes
won't render until the next frame.
So if you want to make a change on the screen for rendering, then you might want to do it before the
screen is drawn.
Otherwise, if you do it further down in the frame, you're going to have to wait till the next frame.
Then it says replication receive jobs, incoming property changes and event firings are applied.
Resume.
Wait weight states, Lua garbage collection.
And then we get to the simulation part of the frame where it's going to do physics.
And there's another event in the run service called stepped, which fires before the internal physics
step.
So it says relative positions of parts joined by motor six.
These are updated part contacts and constraints are updated.
Slash solved.
This can happen multiple times per frame since physics will update at 240Hz.
Based, part touched and related physics events fire after all world steps are complete, blah blah
blah.
This is a bunch of information, but basically after the screen is rendered and physics are done, then
the final event is going to be fired, which is the heartbeat event.
Back in the run service documentation, let's actually take a look at all of the events that we have
available.
And you're going to notice that there's actually quite a few.
We have seven events here.
Now.
The most common ones in the original ones are going to be heartbeat, render stepped and stepped.
These are the three events that we saw in that image that I just showed you from the Task Scheduler
documentation page.
Now post simulation, pre animation, Pre-render and pre simulation are all new events.
And these events are likely going to replace heartbeat render step and stepped post simulation as the
name suggests happens after physics are calculated.
So this event is actually just the same as the heartbeat event.
This is just the new name they gave to it, and this event is probably going to be deprecated in the
future.
The next one we're going to have here is called pre animation.
This is actually a new event that executes or fires before the simulation happens in the game, but
it also happens after the screen is rendered.
So there's a stage in which animations are ran.
And this pre animation event, as the name suggests, happens right before animations are calculated.
This next event, called Pre-render is the same as render step.
This fires, you know before the frame is rendered.
And then once it's all done, then it's going to go and render or draw things onto the screen.
So render step and pre render are the same thing.
And then pre simulation and stepped are also the exact same thing.
So the order in which these events execute are as follows.
The first one is going to be render stepped then stepped and then heartbeat.
And then with the new events what's going to fire first is pre render, then it's going to fire pre
animation.
Then it's going to fire pre simulation.
And then finally it will fire post simulation.
And this all happens in a single frame.
Now to prove the order of these events I have this local script here that is connected to every single
event in the run service.
And I'm using the debug library to create some tags within the micro profiler, so we can go ahead and
take a look at them when we open the Micro Profiler.
But as you can see I'm connected to pre render render stepped stepped pre animation, post simulation,
heartbeat and post simulation.
Oops I said post simulation.
This is pre simulation.
This one's post simulation.
Now the code inside of here is not doing anything special.
This is just so we can kind of push out the frame time.
So we can actually see these tags appear in the micro profiler.
So let's go ahead and do a quick play test of the game.
And let me go ahead and open up the micro profiler.
And then I'm going to pause it pressing control and P.
And then I open the micro profiler using control and F6.
That's how you open the micro profiler.
And then I'm going to pause it.
And this might seem a little bit advanced to you at the moment.
That's okay.
You're going to learn about the micro profiler when we start talking or learning about parallel, will
you?
But that comes, I believe, in a later lecture, but I'm just going to select a frame here.
And what I'm going to do is I'm going to verify the order that things are being executed in.
So what is a good example.
Let me see.
Okay.
Let's let's pick this frame right here.
This is one frame because you can see this one light line and the other white line.
This signifies one frame that was rendered.
So the first thing that's going to happen, as you can see down here, there's our render step work
and our pre render work.
So these are the first two events that fire and happen in the beginning of the frame.
And this happens in the render section.
So as you can see there's this tag here called Pre-render.
And you can see that's where our local scripts were executing.
And then afterwards it's going to do the visualization or rendering and stuff like that.
That's what happens right here.
You can see a tag here called render as well.
So this stuff happens before the frame is rendered.
And then the next one that's going to happen is going to be simulation.
So we can see that this thread was handling simulation.
And let's take a look okay.
Here is our other work.
Unfortunately the tags are a little bit broken here because it's not supposed to be tagged like that.
You can see it says render step work even though this is not render step work.
And if we hover over it you can actually see down in the label it does say pre simulation work.
So I'm not sure why the tag is broken.
I guess that's just a Roblox bug, but as you can see here is our stepped work and here is our pre simulation
work.
So this happens before physics are calculated which occurs right here.
You can see physics stepped.
These were executing right before physics were calculated.
And then we should have the other one that happens before animations which I believe it's this one right
here.
Yes this is pre animation work.
So right here you can see step humanoid step animation.
This is when animations are done or rendered or whatever you want to call it.
But right before we do the animations is when this event is fired, which is our pre animation event.
And then after we do the animation, then it fires the stepped and the pre simulation events.
And then after the physics are done calculating then we hop into heartbeat.
And as you can see here is our heartbeat work.
And here is our post simulation work.
So before we leave the micro profiler, an important thing I want to mention is that all of these run
service events like the Prerender event, the pre animation event, the pre simulation and post simulation
events, all of these have something called a resumption cycle or invocation cycle.
These are points in the frame where any coroutines or scripts that were waiting to be executed are finally
resumed and executed, hence the name resumption cycle.
You can actually see each of these resumption cycles in multiple points in the frame, because they
are tagged with the name deferred threads.
You can see one here for heartbeat.
You can see another one here for stepped or pre simulation.
You can see one here for the pre animation event.
And you can also see one right here for render stepped or pre render.
This means that each of these run service events have their own resumption cycles, for a total of four
resumption cycles in each frame.
There is also an additional resumption cycle specifically for the bind to close method within the data
model, but we won't be focusing on that in this lecture.
Now, to actually see these resumption cycles and action we're going to utilize the task library to
basically push one of these functions or one of these coroutines towards the end of the resumption cycle.
And we can do that by using the task dot defer function and the task library.
So let's do that on heartbeat.
Let me hop back into my local script real quick and inside of the function, listening to my heartbeat
event.
Before I do all of this work I'm going to do is I'm going to call task Dot defer and it says calls slash
resumes a function slash coroutine on the next resumption cycle.
Or basically AKA means it's going to resume it at the end of the current resumption cycle.
So if I do task dot defer function and execute this code in here, you're going to see that this tag
of heartbeat work is going to be pushed towards the end of the heartbeat resumption cycle.
So if we go back and play the game, let me unpause the micro profiler, capture some frames and then
let me pause it again using control and P.
And as you can see, our heartbeat work has moved towards the end of this deferred thread section.
Originally it was the first one, but post simulation is now the first one because we have deferred
heartbeat work towards the end of this resumption cycle.
That's what tasked defer does.
To further demonstrate this, let me get rid of all of these other connections to all the other run
service events, and let's mainly focus on the heartbeat and let's copy this and let's put two more
in here.
But these ones I don't want to defer.
I'm going to have these ones execute immediately.
So we can call this one heartbeat work one.
We'll call this one heartbeat work two and then this one.
Up here we're going to call this heartbeat work last.
So because we're deferring this function to execute at the end of the resumption cycle for the heartbeat
event, this tag in the micro profiler should appear at the very end of the resumption cycle.
So let's capture some frames and then pause.
And then let's go ahead and take a look.
Oh what do you notice.
Here is our deferred thread section.
Here is heartbeat work two heartbeat work one.
Those executed first.
But then as you see the heartbeat work last got pushed to the end of this resumption cycle.
It's the last thing that executes because we used task dot defer.
This task dot defer function works with all of the run service events and their resumption cycles.
So if you're listening to maybe the stepped event or the render stepped event, and you want to push
your function towards the end of the resumption cycle, maybe you need to time something properly.
That's what you can use.
Task dot defer for in case you are having a hard time grasping or understanding resumption cycles.
I also have explained within the course cheat sheet, which is also linked under the resources section
of this lecture.
So if you want to go ahead and check that out after this lecture is over, feel free to do so.
Okay.
So now we're going to get some practice in by using one of the run service events.
I've made this part here.
Feel free to do the same.
And I called it rotate part.
And I make sure to anchor it.
And then I just kind of made it a little bit more elongated.
But what I'm going to do is I'm going to basically have a script that's going to run every single frame,
and we're just going to have this part rotate around and make it look nice and smooth on the screen.
If we look back in the documentation and look at something like the render stepped event, it says the
render step event fires every frame prior to the frame being rendered, which is probably going to be
the event we want to use because we're going to be rotating the part, and we want that to basically
show up before the frame gets rendered.
Now each of these run surface events get passed an argument called Delta time, and according to the
documentation it says delta time is the time in seconds that has elapsed since the previous frame.
So this basically tells you what the time gap is between frames.
So when you render a frame and then you finish rendering the frame, and then you move on to the next
frame, that time it took the previous frame to render and then get to your next frame is what Delta
Time represents.
This number is actually incredibly crucial for allowing you to be able to code logic in your games that
executes properly across multiple different frame rates.
And that's because if somebody is running at 30 frames per second and another person is running at 240
frames per second, this delta time number is going to be different.
In fact, back in studio here with the Micro Profiler open, you can see how long it's taking to render
a single frame.
Right now the average is about seven milliseconds.
So you can basically think of the delta time being around 7.16 milliseconds, because that's how long
it takes to render one frame and then move on to the next frame.
However, that's because I am running at a frame rate of, I believe 240.
Yeah, 240 frames per second.
If I set my frame rate to the default of 60 frames per second.
And then let's reopen the micro profiler.
As you can see, our frame time has completely changed to match that frame rate.
So now instead of averaging around seven milliseconds, we're now hitting about 16 17 milliseconds.
And that would be your new Delta time.
This means if we wanted to do something like a rotate this part every single frame, we needed to be
able to calculate how much we should rotate the part by each frame, depending on what the delta time
is.
Otherwise, if you always rotate the part by the same amount every single frame, no matter what the
delta time is, this part might be rotating faster on some clients, and it might be rotating slower
on other clients.
So what I would like for you to do is I would like for you to create a local script.
You can put in like starter player scripts, and your local script needs to reference your rotating
part, so I'll make a reference to it.
Rotate part is equal to workspace dot rotate part.
And then let me make sure streaming is disabled in the workspace okay good.
It's disabled.
Otherwise I might have to use wait for child here.
And then let's go ahead and grab the run service game.
Get service run service.
And now what I would like for you to do is I would like for you to connect to the render stepped event
and rotate the part every single frame.
And I want you to try using Delta time to make sure that the part rotates at a constant speed, no matter
what the frame rate is.
Now, if you are unsure how to utilize delta Time, then go ahead and attempt this without using Delta
time and I'll show you what to do next.
So pause the lecture right now.
Take a few minutes to work on this and I'll see you in a few.
Okay, so let's try rotating this part using the run service.
So inside of our local script we have a reference to the render step event.
And we're going to connect a function to this event.
And this function is going to be passed Delta time.
But for this first demonstration I'm not going to utilize delta time at all.
So what I need to do is I need to basically just rotate my part around the y axis every single frame.
And a way we could do that is by getting the keyframe of the part and basically adding just a cframe.angles
and rotating it around the y axis.
So we could do times equal, which is plus for keyframes.
frames.
And then we could do c frame dot angles.
And we need to rotate it around the y axis.
How much should we do at each frame.
Because this has to be radians.
Let's say we rotate it by one degree each frame.
So we'll do math dot rad one.
And there is our very basic very simple script.
So if we go and play the game, as you can see our part is rotating by one degree every single frame.
If we open up the micro profiler, this is currently the frame time around 7 to 8 milliseconds.
Now if I open my settings and I switch the maximum frame rate to something lower, like 60 frames per
second, what you're going to notice is now my part is rotating slower, and that's because there's
not as many frames occurring every single second, and because there's not as many frames occurring,
that means this part is only being rotated, I believe 60 degrees per second, while the previous one
was being rotated 240 degrees per second.
So this is a reason why we need to use delta time in our calculations.
So utilizing delta time is very simple.
What we can do is we can basically define a constant.
We can call this rotate speed.
And we can have this number represent how many degrees we want our part to rotate every single second.
So let's say every single second.
We want our part to rotate by 60 degrees no matter the frame rate.
So every single second we're going to rotate by 60 degrees.
So what we need to do is we need to pass that here rotate speed or the 60 degrees.
And now we need to multiply the rotate speed by delta time.
And it's because delta time represents the number that it took from the previous frame.
So rendering the previous frame to reaching this current frame that we're executing in.
So if the delta time is 1/60 of a second, we're going to move or rotate our C frame by 1/60 of this,
which would be one degree.
And then if the delta time changes, maybe the delta time ends up being, I don't know, 1/240 of a
second.
Then we're only going to be rotating the keyframe by 1/240 of 60.
So by incorporating Delta time into our calculation here now the part should always rotate at this exact
same rotate speed, no matter what the frame rate is.
So here we are inside of the game.
Our part is rotating 60 degrees per second, and we're currently running at a frame rate of 60.
Now, let me switch this to a frame rate of 240.
And what do you notice?
Well, I noticed that the part is still rotating at the same speed.
It doesn't matter what I switch my frame rate to if I do 120 still rotating at the same speed.
If I do it at 144, still rotating at the same speed.
So as you can see by using delta time, now our part isn't rotating at different speeds depending on
what the frame rate is.
And this also helps in case maybe your client has like a lag spike.
And the delta time needs to account for that.
By utilizing delta time in your calculations, you are coding correct logic in your games.
Now another important thing I do want to mention is that the render stepped event is not accessible
by server scripts.
So if I were to copy this code and I were to place it inside of a new server script, let me paste it
in there and then let me play the game, you're going to see an error appear.
Render stepped event can only be used from local scripts.
This error only happens with the render step event, which is actually quite interesting because if
you think about the pre render event, that's the same as render steps.
But if we go and run the game, we're not going to have that same error.
And as you can see, our part rotates.
So this leads me to believe that render stepped the stepped event, as well as the heartbeat event,
are all going to be deprecated at some point in the future.
So the alternative events you should be using is going to be run service dot Pre-render.
Pre animation.
pre simulation and post simulation.
Those are going to be the for future new run service events that you should use for any newer projects
you are creating.
Now obviously these new run service events are quite recent because they don't even have any documentation
on the Roblox API website.
So I would keep an eye out for when the old run service events are deprecated and the new ones have
documentation added inside of them.
Alrighty, now let's take a look at some of the methods within the run service.
There are several listed here, but the ones that you'll likely use the most often will be bind to render.
Stepped is client, is server is studio and unbind from render step.
I personally have not used any of the other functions inside of this service, as some of them relate
to, like developing plugins, and I just haven't found a use case for them yet, but that doesn't mean
you will never use them.
Let's take a look at this first one called bind to Render step, which I mentioned earlier, and it
says the bind to render step function binds a custom function to be called at a specific time.
During the render step event.
There are three main arguments for bind.
To render step you got to give a name for your binding, what priority you want it to run at, and what
function needs to be called.
When we encounter the render step phase in a frame, as it is linked to the client's rendering process,
bind to render step can only be called on the client, so you can only do it through local scripts.
So the first parameter is the name.
It's a label for the binding, and it can be used with the unbind from render step function.
If you need to unbind the function and no longer execute it at every single frame.
So that's the purpose of having the name for your binding.
You can use the unbind from render step function to remove the function from the render step event.
The priority of the binding is an integer and determines when during the render step to call the custom
function, the lower this number, the sooner the custom function will be called.
If two bindings have the same priority, the Roblox engine will randomly pick one to run first.
The default Roblox control scripts run with these specific priorities, so player input runs at a priority
of 100 and camera controls runs at a priority of 200.
And for convenience, the render Priority enum can be used to determine the integer value to set a binding.
For example, to make a binding run right before the default camera update, we simply subtract one
from the camera priority level.
So in this example, they give here.
If you want this function to run right before the default or core Roblox scripts updates the camera,
you can access the enum dot render priority dot camera, access the value of that enum and subtract
it by one.
So now this function will execute right before the camera updates.
If you want your function to execute right after the camera updates, maybe you want to override whatever
the core camera scripts do.
Then you can do plus one instead of minus one.
The function you pass to the bind to render step function is also going to be called and given the delta
time, in case you need to use it for making sure the logic of your code works across multiple different
frame rates.
The rest of these methods are pretty straightforward.
For example, is server is a function that simply returns a boolean and it tells you whether or not
you're currently running on the server.
So if you have a server script and you call the is server function, it's going to return true.
If you're in a local script and you call is server, it's going to return false.
If you're in a module script that got required by a server script, it's going to return true.
And likewise, if the module script was required by a local script, this is going to return false.
Same thing with the Is client function.
This one will also return true or false if the current environment is running on the client.
So it's basically just the opposite of is server and then is studio lets you know if you are currently
running in studio.
So whether you're in a local script or a server script, if you're currently play testing or running
the game within studio, this will always return true.
So just as a quick example here inside of my local script, let's just go ahead and print out the run
service is client.
And then let's go to our server script and let's print the other function which is run service is server.
We're going to print this one out and let's see what values both of these return.
They should both print true because this is a server script and this is a local script.
And then on the client side let's also go ahead and utilize the run service bind to render step.
Let's give it a name of a rotate part.
So we're going to move this from the server.
Let me copy this code and just get rid of that on the server.
And we're going to have a priority of let's actually let's take a look at the enum render priority.
What we got in here.
We got character.
This should run after camera.
We got the camera last first input.
Um I'm not sure exactly when we should rotate this part.
I don't think it really matters, does it?
Does it really matter?
Let's do it after the camera updates.
Why not?
So we're going to do camera.
We're going to get the value and we're just going to do plus one.
And then we need to pass our function to make it really easy.
I'm just going to put a lambda function here.
Let's paste that rotating code.
Uh we're going to need to access delta time here.
Delta time.
And then we also need to define our rotate speed.
Let's make it a little bit faster.
Let's do like 240 degrees per second okay.
There we go.
And we also need a variable reference to our rotate part.
So workspace dot rotate part okay.
Perfect.
So now we have basically converted our code to use the bind to render step function, which also means
at some point in the future, if we no longer want to have this function executing at render step,
let's say five seconds in the future, for some reason, we want to no longer execute this code right
here.
We can simply use the unbind from render step and give it the name which was our rotate part.
So let's go ahead and hop in, play test the game.
And as you can see both of those scripts printed true.
Server printed true.
The client printed true for the Is client method, and then it printed true on the server for the Is
server method, which makes sense.
My part is no longer rotating because I believe that function got unblinded from or unbound from render
step.
So actually let's make that delay a little bit longer.
Let's make the delay like 10s.
And let's hop back into the game and let's see if our parts rotate.
All right.
Our parts rotating rotating.
And eventually it should stop here at any second.
There we go.
Now our part is no longer rotating because the function has no longer been binded to the run service
render stepped event.
Now, I'm actually curious to see if Roblox will provide another function in the future, since I believe
render stepped is going to be deprecated.
Maybe they'll have like a function called run service bind to Prerender.
Maybe they'll have a function like that in the future that literally does the exact same thing.
Who knows.
We'll see what happens.
Alrighty.
And that's basically the gist of the run service.
It's a simple service that allows you to schedule and execute code on a frame by frame basis.
And they give us, you know, several events to be able to execute code at certain points in the frame,
like if you want to run code before the screen is rendered, you can use pre render if you want to do
it before animations are calculated or whatever.
Use pre animation if you want to do it before the physics calculations, use pre simulation or if you
want to schedule code at the very end of the frame, then you can use post simulation.
That's going to be everything from me for this lecture.
Thank you for watching and I'll see you next time.